/* 
 * File:   mem.h
 * Author: skyro
 *
 * Created on December 17, 2010, 7:02 PM
 */

#ifndef _MEM_H
#define	_MEM_H

#include <cstdlib>
#include "../common/types.h"

// abstract allocator which every customed allocator should be inherent from
class Allocator
{
public:

    virtual void* allocate(U32 t, U32 align) = 0;
    virtual void deallocate(void* p) = 0;
   /* virtual size_t alloc_size(void* p) = 0;*/

};

// heap allocator responsible of allocating memory from static heap
// this heap allocator uses stack-based allocation and deallocation
// which means it has to deallocate memories in the opposite order
// of their allocations.

class HeapAllocator : public Allocator
{
private:

    // How many free memory are available
    U32 mFreeMemory;

    // the base address of heap
    U32 mBase;

    // the top address of heap
    U32 mBrk;

public:

    //  methods of allocating space for new allocator
    template<class T>
    T* make_new(const char* allocatorName)
    {
        return new(allocate(sizeof(T), ALIGNOF(T))) T(allocatorName);
    }

    template<class T>
    T* make_new(const char* allocatorName, U32 memorySize, U32 chunkSize)
    {
        return new(allocate(sizeof(T), ALIGNOF(T))) T(allocatorName, memorySize, chunkSize);
    }

    // method of deallocator space for allocator
    template<class T>
    void make_delete(T* p)
    {
        if(p)
        {
           p->~T();
           deallocate(p);
        }
    }

    void* allocate(U32 t, U32 align);
    void deallocate(void* p);
    /*size_t alloc_size(void* p);*/

    HeapAllocator(const char* name, char* base, U32 free_size);
};

class MemoryChunk
{
public:

    // pointer of the actual data
    U8* data;

    // the size of data this chunk can manage,
    // i.e. the memory from this chunk to end.
    size_t dataSize;

    // the size of memory used in one allocation
    size_t usedSize;

    MemoryChunk():data(0),dataSize(0),usedSize(0){};
};

// pool allocator is non-extendable allocator
// which means it has to be allocated with
// large enough memory at it's construction
// when memory is running out, it generates
// an interrupt.
// Because memory allocation is expensive,
// when memory is out, we can't afford allocating
// new memory.
class PoolAllocator : public Allocator
{
public:
    void* allocate(U32 t, U32 align);
    void deallocate(void* p);
    PoolAllocator(const char* name, const size_t& totalMemorySize, const size_t& chunkSize);
private:

    // given a memory size, determine how many chunks are needed
    U32 CalculateNeededChunks(const size_t& memorysize)
    {
        return memorysize / mChunkSize + (0!=(memorysize%mChunkSize)?1:0);
    }

    // allocate memory for this allocator and link them to the chunks
    void AllocateMemory(const size_t& memorysize);

    //link data to chunks
    void LinkChunkToData(U8* pMemory, MemoryChunk* pChunks, U32 chunkCount);

    // allocate unaligned memory
    void* AllocateUnalignedMemory(U32 size);

    // search the available chunks to find memory to hold the data
    MemoryChunk* FindChunkToHoldMemory(U32 memorySize);

    ~PoolAllocator()
    {
        free(mpFirstChunk->data);
        delete mpFirstChunk;
    }
private:
    MemoryChunk* mpFirstChunk;
    MemoryChunk* mpLastChunk;

    // current chunk from which the new memory search starts
    MemoryChunk* mpCurChunk;
    size_t mTotalMemorySize;
    size_t mUsedMemorySize;
    size_t mFreeMemorySize;
    size_t mChunkSize;
};

#endif	/* _MEM_H */

